Examples

Basic Trap

"""Simple trap optimization.

A very basic use-case, finding the correct phases to levitate a bead centered
5 cm above a 9x9 element rectangular array, then inspecting the resultant field.
"""

import numpy as np
import levitate

pos = np.array([0, 0, 80e-3])
array = levitate.arrays.RectangularArray(9)
phases = array.focus_phases(pos) + array.signature(stype='twin') + 0.2 * np.random.uniform(-np.pi, np.pi, array.num_transducers)
start = levitate.utils.complex(phases)

# Create the cost functions and minimize them.
point = levitate.fields.GorkovLaplacian(array) * (-100, -100, -1) + abs(levitate.fields.Pressure(array)) * 1e-3
results = levitate.optimization.minimize(point@pos, array, start_values=start)

# Visualize the field.
array.visualize[0] = ['Signature', pos]
array.visualize.append('Pressure')
array.visualize(results).write_html(file='basic_trap.html', include_mathjax='cdn')

Field Superposition

"""Superposition of two fields.

A more advanced usage, designed to create a field with a levitation trap
and a haptics focus point.
"""

import numpy as np
import levitate

array = levitate.arrays.RectangularArray((21, 12))
trap_pos = np.array([-20e-3, 0, 60e-3])
haptics_pos = np.array([40e-3, 0, 90e-3])
phases = array.focus_phases(trap_pos) + array.signature(trap_pos, stype='twin') + 0.2 * np.random.uniform(-np.pi, np.pi, array.num_transducers)
start = levitate.utils.complex(phases)

# The fields are superposed using mutual quiet zones, created by minimizing the
# pressure and velocity at the secondary point in each field.
# We will need three fields, calculating the pressure magnitude,
# the velocity magnitude, and the stiffenss of the trap.
p = abs(levitate.fields.Pressure(array))
v = abs(levitate.fields.Velocity(array))
s = levitate.fields.RadiationForceStiffness(array)

# The levitation trap is found using a minimization sequence.
# First the phases are optimized for just a trap,
# then the phases and amplitudes are optimized to include the quiet zone.
trap_result = levitate.optimization.minimize(
    [
        (s * (1, 1, 1) + p * 1)@trap_pos,
        (s * (1, 1, 1) + p * 1)@trap_pos + (v * (1e3, 1e3, 1e3) + p * 1)@haptics_pos
    ],
    array, start_values=start, variable_amplitudes=[False, True])[-1]

# The haptics point can be created using a simple focusing algorithm,
# so we can optimize for the inclusion of the quiet zone straight away.
# To retain the focus point we set a negative weight for the pressure,
# i.e. maximizing the pressure.
start = levitate.utils.complex(array.focus_phases(haptics_pos))
haptics_result = levitate.optimization.minimize(
    p * (-1)@haptics_pos + (p * 1 + v * (1e3, 1e3, 1e3))@trap_pos,
    array, start_values=start, variable_amplitudes=True)

# Visualize the individual fields, as well as the compound field.
array.visualize.append('pressure')
array.visualize(
    trap_result, haptics_result, haptics_result * 0.3 + trap_result * 0.7,
    labels=['Trap', 'Haptics', 'Combined']
).write_html(file='two_fields.html', include_mathjax='cdn')

Complex Setups

"""Incorporating transducer models, reflectors, and doublesided arrays.

The setup shown here is a doublesided array where the two halves
are standing vertically 3 cm above a reflecting surface.
The two halves are separated by 20 cm, and each side has 50 elements.
The transducers are modeled as circular pistons, and the reflection is
included by using the `ReflectingTransducer ` meta-class.

In this example no optimization is done, but all optimization functions
support complex arrangements like this one.
"""

import numpy as np
import levitate

transducer = levitate.transducers.TransducerReflector(
    levitate.transducers.CircularPiston, effective_radius=3e-3,
    plane_intersect=(0, 0, 0), plane_normal=(0, 0, 1))

array = levitate.arrays.DoublesidedArray(
    levitate.arrays.RectangularArray, separation=200e-3,
    normal=(1, 0, 0), offset=(0, 0, 50e-3),
    shape=(5, 10), transducer=transducer)

phases = array.focus_phases(np.array([25e-3, 0, 40e-3]))
amps = levitate.utils.complex(phases)
array.visualize.zlimits = (0, 0.1)
array.visualize.append('Pressure')
array.visualize.append('Velocity')
array.visualize(amps).write_html(file='complex_setup.html', include_mathjax='cdn')

Force Diagrams

"""Visualizing the force around a trap.

Convenient methods to show how the radiation force changes
along the Cartesian axes for different sizes of the beads.
"""

import numpy as np
import levitate
from levitate.visualizers import ForceDiagram

pos = np.array([0, 0, 60e-3])
array = levitate.arrays.RectangularArray(16)
amps = levitate.utils.complex(array.focus_phases(pos) + array.signature(stype='twin'))

diagram = ForceDiagram(array)
radii = [1e-3, 2e-3, 4e-3, 8e-3, 16e-3]
for radius in radii:
    diagram.append([pos, {'radius': radius, 'name': '{} mm'.format(radius * 1e3)}])
diagram(amps).write_html(file='force_diagrams.html', include_mathjax='cdn')